using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace TimeWar
{
    namespace Octree
    {

        public class Octree<T> : IOctree<T>
        {
            protected internal OctreeNode<T> top;

            public Octree()
                : this(1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 20, OctreeNode<T>.NO_MIN_SIZE)
            {
            }

            public Octree(float xMax, float xMin, float yMax, float yMin, float zMax, float zMin, int maxItems)
                : this(xMax, xMin, yMax, yMin, zMax, zMin, maxItems, OctreeNode<T>.NO_MIN_SIZE)
            {
            }

            public Octree(int up, int left, int down, int right, int Front, int Back, int maxItems)
                : this((float)up, (float)left, (float)down, (float)right, (float)Front, (float)Back, maxItems, OctreeNode<T>.DEFAULT_MIN_SIZE)
            {
            }

            public Octree(float xMax, float xMin, float yMax, float yMin, float zMax, float zMin, int maxItems, float minSize)
            {
                top = new OctreeNode<T>(xMax, xMin, yMax, yMin, zMax, zMin, maxItems, minSize);
            }

            #region Add Node

            /// <summary> Add a object into the tree at a location.
            /// </summary>
            /// <param name="x">up-down location in Octree Grid</param>
            /// <param name="y">left-right location in Octree Grid</param>
            /// <param name="z">front-back location in Octree Grid</param>
            /// <returns> true if the insertion worked. </returns>

            public bool AddNode(Vector3 vector, T obj)
            {
                return top.AddNode(vector, obj);
            }

            #endregion Add Node


            #region Remove Node
            /// <summary> Remove a object out of the tree at a location. </summary>
            /// <param name="x">up-down location in Octree Grid (x, y)</param>
            /// <param name="y">left-right location in Octree Grid (y, x)</param>
            /// <returns> the object removed, null if the object not found.
            /// </returns>
            public T RemoveNode(Vector3 vector, T obj)
            {
                return top.RemoveNode(vector, obj);
            }
            #endregion

            #region Get Node

            /// <summary> Get an object closest to a x/y. </summary>
            /// <param name="x">up-down location in Octree Grid (x, y)</param>
            /// <param name="y">left-right location in Octree Grid (y, x)</param>
            /// <returns> the object that was found.</returns>
            public T GetNode(Vector3 vector)
            {
                return top.GetNode(vector);
            }

            /// <summary> Get an object closest to a x/y, within a maximum distance.
            /// 
            /// </summary>
            /// <param name="x">up-down location in Octree Grid (x, y)
            /// </param>
            /// <param name="y">left-right location in Octree Grid (y, x)
            /// </param>
            /// <param name="withinDistance">the maximum distance to get a hit, in
            /// decimal degrees.
            /// </param>
            /// <returns> the object that was found, null if nothing is within
            /// the maximum distance.
            /// </returns>
            public T GetNode(Vector3 vector, double withinDistance)
            {
                return top.GetNode(vector, withinDistance);
            }

            /// <summary> Get all the objects within a bounding box.
            /// 
            /// </summary>
            /// <param name="Top">top location in Octree Grid (x, y)
            /// </param>
            /// <param name="Left">left location in Octree Grid (y, x)
            /// </param>
            /// <param name="Bottom">lower location in Octree Grid (x, y)
            /// </param>
            /// <param name="Right">right location in Octree Grid (y, x)
            /// </param>
            /// <returns> ArrayList of objects.
            /// </returns>
            public List<T> GetNode(float xMax, float xMin, float yMax, float yMin, float zMax, float zMin)
            {
                return GetNode(xMax, xMin, yMax, yMin, zMax, zMin, new List<T>(100));
            }
            public List<T> GetNode(double xMax, double xMin, double yMax, double yMin, double zMax, double zMin)
            {
                return GetNode(xMax, xMin, yMax, yMin, zMax, zMin, new List<T>(10));
            }

            /// <summary> Get all the objects within a bounding box, and return the
            /// objects within a given Vector.
            /// 
            /// </summary>
            /// <param name="Top">top location in Octree Grid (x, y)
            /// </param>
            /// <param name="Left">left location in Octree Grid (y, x)
            /// </param>
            /// <param name="Bottom">lower location in Octree Grid (x, y)
            /// </param>
            /// <param name="Right">right location in Octree Grid (y, x)
            /// </param>
            /// <param name="vector">a vector to add objects to.
            /// </param>
            /// <returns> ArrayList of objects.
            /// </returns>
            public List<T> GetNode(float xMax, float xMin, float yMax, float yMin, float zMax, float zMin, List<T> nodes)
            {
                if (nodes == null)
                    nodes = new List<T>();

                if (xMin > xMax || (Math.Abs(xMin - xMax) < 1e-6))
                    return top.GetNode(xMax, xMin, yMax, yMin, zMax, zMin, top.GetNode(xMax, 0, yMax, yMin, zMax, zMin, nodes));
                else
                    return top.GetNode(xMax, xMin, yMax, yMin, zMax, zMin, nodes);
            }
            /// <summary>
            /// 
            /// </summary>
            /// <param name="xMax"></param>
            /// <param name="xMin"></param>
            /// <param name="yMax"></param>
            /// <param name="yMin"></param>
            /// <param name="zMax"></param>
            /// <param name="zMin"></param>
            /// <param name="nodes"></param>
            /// <returns></returns>
            public List<T> GetNode(double xMax, double xMin, double yMax, double yMin, double zMax, double zMin, List<T> nodes)
            {
                if (nodes == null)
                    nodes = new List<T>();

                if (xMin > xMax || (Math.Abs(xMin - xMax) < 1e-6))
                    return top.GetNode(xMax, xMin, yMax, yMin, zMax, zMin, top.GetNode(xMax, 0, yMax, yMin, zMax, zMin, nodes));
                else
                    return top.GetNode(xMax, xMin, yMax, yMin, zMax, zMin, nodes);
            }

            #endregion

            #region Get Nodes
            /// <summary> Get an object closest to a x/y, within a maximum distance.
            /// 
            /// </summary>
            /// <param name="x">up-down location in Octree Grid (x, y)
            /// </param>
            /// <param name="y">left-right location in Octree Grid (y, x)
            /// </param>
            /// <param name="withinDistance">the maximum distance to get a hit, in
            /// decimal degrees.
            /// </param>
            /// <returns> the objects that were found  within the maximum radius.
            /// </returns>
            public List<T> GetNodes(Vector3 vector, double radius)
            {
                return top.GetNodes(vector, radius);
            }

            /// <summary> Get an object closest to a x/y, within a maximum distance./// </summary>
            /// <param name="x">up-down location in Octree Grid (x, y)</param>
            /// <param name="y">left-right location in Octree Grid (y, x)</param>
            /// <param name="withinDistance">the maximum distance to get a hit, in
            /// decimal degrees.</param>
            /// <returns> the objects that were found  within the maximum radius.</returns>
            public List<T> GetNodes(Vector3 vector, double MinRadius, double MaxRadius)
            {
                return top.GetNodes(vector, MinRadius, MaxRadius);
            }
            #endregion

            /// <summary>Clear the tree. </summary>
            public void Clear()
            {
                top.Clear();
            }

        }
    }

}